#include "stdafx.h"
#include <tuple>
#include "../engine/Wall.hpp"
#include "../engine/WallPtrVector.hpp"
#include "../engine/Building.hpp"
#include "../engine/MapRenderer.hpp"
#include "../engine/Map.hpp"
#include "../Engine/BuildingMaterialManager.hpp"
#include "../Engine/BuildingRenderer.hpp"

// Define if normals need to be also rendered
#define HE_WALL_NORMALS_SHOW 0

namespace he
{
	BuildingRenderer::BuildingRenderer(Map const &Map_, MapRenderer const &MapRenderer_, Ogre::SceneManager &SceneManager, BuildingMaterialManager const &BuildingMaterialManager_)
		: m_Map(Map_),
		m_MapRenderer(MapRenderer_),
		m_SceneManager(SceneManager),
		m_BuildingMaterialManager(BuildingMaterialManager_)
	{
	}

	void BuildingRenderer::add(Building const &Building_)
	{
		// For now default height
		static Ogre::Real const height = 2;

#if HE_WALL_NORMALS_SHOW == 1
		typedef std::tuple<Ogre::Vector3, Ogre::Vector3> NormalTuple_t;
		std::vector<NormalTuple_t> normal_vec;
#endif

		// Get walls
		WallPtrVector_t const &walls = Building_.walls();
		// Get base tile of building
		TerrainTile const *p_base_tile = &Building_.origin();
		// Material type
		std::uint_least8_t const material = Building_.material();
		// Create manual object
		Ogre::ManualObject *p_object = m_SceneManager.createManualObject();
		p_object->begin(m_BuildingMaterialManager.material_name(material));
		for(auto it = walls.begin(), end = walls.end(); it != end; ++it)
		{
			assert(p_base_tile);
			// Get all tile vertex positions
			Ogre::Vector3 const tile_v0 = m_MapRenderer.tile_vertex(*p_base_tile, 0);
			Ogre::Vector3 const tile_v1 = m_MapRenderer.tile_vertex(*p_base_tile, 1);
			Ogre::Vector3 const tile_v2 = m_MapRenderer.tile_vertex(*p_base_tile, 2);
			Ogre::Vector3 const tile_v3 = m_MapRenderer.tile_vertex(*p_base_tile, 3);
			// Texture coordinate for outside
			TextureAtlasInfo_t const texture_info_outside = m_BuildingMaterialManager.variant_info_outside(material, (*it)->variant());
			Ogre::Vector2 const out_tex_coord_0_0(std::get<0>(texture_info_outside), std::get<1>(texture_info_outside));
			Ogre::Vector2 const out_tex_coord_0_1(std::get<0>(texture_info_outside), std::get<1>(texture_info_outside) + std::get<3>(texture_info_outside));
			Ogre::Vector2 const out_tex_coord_1_0(std::get<0>(texture_info_outside) + std::get<2>(texture_info_outside), std::get<1>(texture_info_outside));
			Ogre::Vector2 const out_tex_coord_1_1(std::get<0>(texture_info_outside) + std::get<2>(texture_info_outside), std::get<1>(texture_info_outside) + std::get<3>(texture_info_outside));
			// Texture coordinate for inside
			TextureAtlasInfo_t const texture_info_inside = m_BuildingMaterialManager.variant_info_inside(material, (*it)->variant());
			Ogre::Vector2 const in_tex_coord_0_0(std::get<0>(texture_info_inside), std::get<1>(texture_info_inside));
			Ogre::Vector2 const in_tex_coord_0_1(std::get<0>(texture_info_inside), std::get<1>(texture_info_inside) + std::get<3>(texture_info_inside));
			Ogre::Vector2 const in_tex_coord_1_0(std::get<0>(texture_info_inside) + std::get<2>(texture_info_inside), std::get<1>(texture_info_inside));
			Ogre::Vector2 const in_tex_coord_1_1(std::get<0>(texture_info_inside) + std::get<2>(texture_info_inside), std::get<1>(texture_info_inside) + std::get<3>(texture_info_inside));
			// Generate vertices
			// All walls begins from left top corner counter-clock wise
			// from position as looking directly to wall from outside the
			// building.
			switch((*it)->orientation())
			{

			case Wall::OD_NS:
				{
					// South
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// North
					Ogre::Vector3 const v1(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v0.z + ((tile_v3.z - tile_v0.z) / 2));

					Ogre::Vector3 const normal = (v1 - v2).crossProduct(Ogre::Vector3::UNIT_Y).normalisedCopy();

					// North Up - Inside
					p_object->position(v1);
					p_object->textureCoord(in_tex_coord_0_0);
					p_object->normal(normal);
					// North Down - Inside
					p_object->position(v1.x, 0, v1.z);
					p_object->normal(normal);
					p_object->textureCoord(in_tex_coord_0_1);
					// South Down - Inside
					p_object->position(v2.x, 0, v2.z);
					p_object->normal(normal);
					p_object->textureCoord(in_tex_coord_1_1);
					// South Up - Inside
					p_object->position(v2);
					p_object->normal(normal);
					p_object->textureCoord(in_tex_coord_1_0);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), normal));
					normal_vec.push_back(NormalTuple_t(v2, normal));
#endif

					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// North (0, 2, 1)
					p_object->triangle(vertex_count - 4 , vertex_count - 2, vertex_count - 3);
					// South (3, 2, 0)
					p_object->triangle(vertex_count - 1, vertex_count - 2,  vertex_count - 4);
				}
				break;
				// West <-> East
				// --
			case Wall::OD_WE:
				{
					// West
					Ogre::Vector3 const v1(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// East
					Ogre::Vector3 const v2(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v3.z + ((tile_v2.z - tile_v3.z) / 2));
					// Normal
					Ogre::Vector3 const normal = (Ogre::Vector3(v2.x, 0, v2.z) - Ogre::Vector3(v1.x, 0, v1.z)).crossProduct(Ogre::Vector3::UNIT_Y).normalisedCopy();

					// West Up - Outside
					p_object->position(v1);
					p_object->normal(normal);
					p_object->textureCoord(out_tex_coord_0_0);
					// West Down - Outside
					p_object->position(v1.x, 0, v1.z);
					p_object->normal(normal);
					p_object->textureCoord(out_tex_coord_0_1);
					// East Down - Outside
					p_object->position(v2.x, 0, v2.z);
					p_object->normal(normal);
					p_object->textureCoord(out_tex_coord_1_1);
					// East Up - Outside
					p_object->position(v2);
					p_object->normal(normal);
					p_object->textureCoord(out_tex_coord_1_0);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), normal));
					normal_vec.push_back(NormalTuple_t(v2, normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// West (0, 1, 3)
					p_object->triangle(vertex_count - 4 , vertex_count - 3, vertex_count - 1);
					// East (1, 2, 3)
					p_object->triangle(vertex_count - 3, vertex_count - 2,  vertex_count - 1);
				}
				break;
			case Wall::OD_SE:
				{
					// South Center
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// South Side
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// East Side
					Ogre::Vector3 const v3(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v3.z + ((tile_v2.z - tile_v3.z) / 2));
					// East Center
					Ogre::Vector3 const v4(tile_v2.x, height, tile_v1.z);

					Ogre::Vector3 const normal_south = (v2 - v1).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();
					Ogre::Vector3 const normal_east = Ogre::Vector3::NEGATIVE_UNIT_Y.crossProduct(v3 - v4).normalisedCopy();

					// South Centre Up - Outside
					p_object->position(v1);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_0.y);
					p_object->normal(normal_south);
					// South Centre Down - Outside
					p_object->position(v1.x, 0, v1.z);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_1.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_1.y);
					p_object->normal(normal_south);
					// South Side Down - Outside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(out_tex_coord_0_1);
					p_object->normal(normal_south);
					// South Side Up - Outside
					p_object->position(v2);
					p_object->textureCoord(out_tex_coord_0_0);
					p_object->normal(normal_south);

					// East Side Up - Outside
					p_object->position(v3);
					p_object->textureCoord(out_tex_coord_1_0);
					p_object->normal(normal_east);
					// East Side Down - Outside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(out_tex_coord_1_1);
					p_object->normal(normal_east);
					// East Center Down - Outside
					p_object->position(v4.x, 0, v4.z);
					// Ending half texture
					p_object->textureCoord(out_tex_coord_0_1.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_1.y);
					p_object->normal(normal_east);
					// East Center Up - Outside
					p_object->position(v4);
					// Ending half texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_0.y);
					p_object->normal(normal_east);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, normal_south));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), normal_south));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), normal_south));
					normal_vec.push_back(NormalTuple_t(v2, normal_south));

					normal_vec.push_back(NormalTuple_t(v3, normal_east));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v3.z), normal_east));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), normal_east));
					normal_vec.push_back(NormalTuple_t(v4, normal_east));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// South Side (3, 2, 1)
					p_object->triangle(vertex_count - 5, vertex_count - 6, vertex_count - 7);
					// South Center (3, 1, 0)
					p_object->triangle(vertex_count - 5, vertex_count - 7, vertex_count - 8);
					// West Side (6, 5, 4)
					p_object->triangle(vertex_count - 2, vertex_count - 3, vertex_count - 4);
					// West Center (6, 4, 7)
					p_object->triangle(vertex_count - 2, vertex_count - 4, vertex_count - 1);
				}
				break;
				// Corner first, north part, east part
			case Wall::OD_NE:
				{
					// North Center
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// North Side
					Ogre::Vector3 const v2(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// East Side
					Ogre::Vector3 const v3(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// East Center
					Ogre::Vector3 const v4(tile_v0.x, height, tile_v1.z);

					// North normal
					Ogre::Vector3 const north_normal = Ogre::Vector3::NEGATIVE_UNIT_Y.crossProduct(v2 - v1).normalisedCopy();
					// West normal
					Ogre::Vector3 const east_normal = Ogre::Vector3::NEGATIVE_UNIT_Y.crossProduct(v3 - v4).normalisedCopy();

					// North Center Up - Inside
					p_object->position(v1);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_0.y);
					p_object->normal(north_normal);
					// North Center Down - Inside
					p_object->position(v1.x, 0, v1.z);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_1.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_1.y);
					p_object->normal(north_normal);
					// North Side Down - Inside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(in_tex_coord_1_1);
					p_object->normal(north_normal);
					// North Side Up - Inside
					p_object->position(v2);
					p_object->textureCoord(in_tex_coord_1_0);
					p_object->normal(north_normal);

					// East Side Up - outside
					p_object->position(v3);
					p_object->textureCoord(out_tex_coord_1_0);
					p_object->normal(east_normal);
					// East Side Down - outside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(out_tex_coord_1_1);
					p_object->normal(east_normal);
					// East Center Down - outside
					p_object->position(v4.x, 0, v4.z);
					// Ending half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_1.y);
					p_object->normal(east_normal);
					// East Center Up - outside
					p_object->position(v4);
					// Ending half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_0.y);
					p_object->normal(east_normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), north_normal));
					normal_vec.push_back(NormalTuple_t(v2, north_normal));

					normal_vec.push_back(NormalTuple_t(v3, east_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), east_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), east_normal));
					normal_vec.push_back(NormalTuple_t(v4, east_normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// North Center (0, 1, 3);
					p_object->triangle(vertex_count - 8, vertex_count - 7, vertex_count - 5);
					// North Side (1, 2, 3)
					p_object->triangle(vertex_count - 7, vertex_count - 6, vertex_count - 5);
					// East Center (7, 6, 5);
					p_object->triangle(vertex_count - 1, vertex_count - 2, vertex_count - 3);
					// East Side (7, 5, 4);
					p_object->triangle(vertex_count - 1, vertex_count - 3, vertex_count - 4);
				}
				break;
				// Centre, west, north
			case Wall::OD_WN:
				{
					// Centre
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// West
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// North
					Ogre::Vector3 const v3(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// Centre
					Ogre::Vector3 const v4(tile_v0.x, height, tile_v1.z);

					// North normal
					Ogre::Vector3 const north_normal = (v4 - v3).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();
					// West normal
					Ogre::Vector3 const west_normal = Ogre::Vector3::NEGATIVE_UNIT_Y.crossProduct(v1 - v2).normalisedCopy();

					// West Center Up - Outside
					p_object->position(v1);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_0.y);
					p_object->normal(west_normal);
					// West Center Down - Outside
					p_object->position(v1.x, 0, v1.z);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_1.y);
					p_object->normal(west_normal);
					// West Side Down - Outside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(out_tex_coord_0_1);
					p_object->normal(west_normal);
					// West Side Up - Outside
					p_object->position(v2);
					p_object->textureCoord(out_tex_coord_0_0);
					p_object->normal(west_normal);

					// North wall Up - Outside
					p_object->position(v3);
					p_object->normal(north_normal);
					p_object->textureCoord(out_tex_coord_1_0);
					// North Wall Down - Outside
					p_object->position(v3.x, 0, v3.z);
					p_object->normal(north_normal);
					p_object->textureCoord(out_tex_coord_1_1);
					// North Center Down - Outside
					p_object->position(v4.x, 0, v4.z);
					// Ending half of texture
					p_object->normal(north_normal);
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_1.y);
					// North Center Up - Outside
					p_object->position(v4);
					// Ending half of texture
					p_object->normal(north_normal);
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_0.y);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), west_normal));
					normal_vec.push_back(NormalTuple_t(v2, west_normal));

					normal_vec.push_back(NormalTuple_t(v3, north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), north_normal));
					normal_vec.push_back(NormalTuple_t(v4, north_normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// West Center (3, 1, 0);
					p_object->triangle(vertex_count - 5, vertex_count - 7, vertex_count - 8);
					// West Side (2, 1, 3);
					p_object->triangle(vertex_count - 6, vertex_count - 7, vertex_count - 5);
					// North Centre (7, 6, 5);
					p_object->triangle(vertex_count - 1, vertex_count - 2, vertex_count - 3);
					// North Side (7, 5, 4);
					p_object->triangle(vertex_count - 1, vertex_count - 3, vertex_count - 4);
				}
				break;
				// Centre, south, west
			case Wall::OD_SW:
				{
					// South Center
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// South Side
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// West Side
					Ogre::Vector3 const v3(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// West Center
					Ogre::Vector3 const v4(tile_v2.x, height, tile_v3.z);

					Ogre::Vector3 const south_normal = (v2 - v1).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();
					Ogre::Vector3 const west_normal = (v3 - v4).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();

					// South Center Up - Outside
					p_object->position(v1);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_0.y);
					p_object->normal(south_normal);
					// South Center Down - Outside
					p_object->position(v1.x, 0, v1.z);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_1.y);
					p_object->normal(south_normal);
					// South Side Down - Outside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(out_tex_coord_0_1);
					p_object->normal(south_normal);
					// South Side Up - Outside
					p_object->position(v2);
					p_object->textureCoord(out_tex_coord_0_0);
					p_object->normal(south_normal);

					// West Side Up - Inside
					p_object->position(v3);
					p_object->textureCoord(in_tex_coord_0_0);
					p_object->normal(west_normal);
					// West Side Down - Inside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(in_tex_coord_0_1);
					p_object->normal(west_normal);
					// West Center Down - Inside
					p_object->position(v4.x, 0, v4.z);
					// Beginning half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_1_1.y);
					p_object->normal(west_normal);
					// West Center Up - Inside
					p_object->position(v4);
					// Beginning half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_1_0.y);
					p_object->normal(west_normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, south_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), south_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), south_normal));
					normal_vec.push_back(NormalTuple_t(v2, south_normal));

					normal_vec.push_back(NormalTuple_t(v3, west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), west_normal));
					normal_vec.push_back(NormalTuple_t(v4, west_normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// South Center(3, 1, 0);
					p_object->triangle(vertex_count - 5, vertex_count - 7, vertex_count - 8);
					// South Side (3, 2, 1);
					p_object->triangle(vertex_count - 5, vertex_count - 6, vertex_count - 7);
					// West Side (4, 5, 6);
					p_object->triangle(vertex_count - 4, vertex_count - 3, vertex_count - 2);
					// West Center (4, 6, 7);
					p_object->triangle(vertex_count - 4, vertex_count - 2, vertex_count - 1);
				}
				break;
				// South, North
			case Wall::OD_SN:
				{
					// South
					Ogre::Vector3 const v1(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// North
					Ogre::Vector3 const v2(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v0.z + ((tile_v3.z - tile_v0.z) / 2));

					Ogre::Vector3 const normal = (v2 - v1).crossProduct(Ogre::Vector3::UNIT_Y).normalisedCopy();

					// South Up - Outside
					p_object->position(v1);
					p_object->textureCoord(out_tex_coord_0_0);
					p_object->normal(normal);
					// South Down - Outside
					p_object->position(v1.x, 0, v1.z);
					p_object->textureCoord(out_tex_coord_0_1);
					p_object->normal(normal);
					// North Down - Outside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(out_tex_coord_1_1);
					p_object->normal(normal);
					// North Up - Outside
					p_object->position(v2);
					p_object->textureCoord(out_tex_coord_1_0);
					p_object->normal(normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), normal));
					normal_vec.push_back(NormalTuple_t(v2, normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// South (0, 1, 2)
					p_object->triangle(vertex_count - 4 , vertex_count - 3, vertex_count - 2);
					// North (0, 2, 3)
					p_object->triangle(vertex_count - 4, vertex_count - 2,  vertex_count - 1);
				}
				break;
			case Wall::OD_EW:
				{
					// West
					Ogre::Vector3 const v1(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// East
					Ogre::Vector3 const v2(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v3.z + ((tile_v2.z - tile_v3.z) / 2));

					// Normal
					Ogre::Vector3 const normal = Ogre::Vector3::NEGATIVE_UNIT_Y.crossProduct(v2 - v1).normalisedCopy();

					// West Up - Inside
					p_object->position(v1);
					p_object->textureCoord(in_tex_coord_1_0);
					p_object->normal(normal);
					// West Down - Inside
					p_object->position(v1.x, 0, v1.z);
					p_object->textureCoord(in_tex_coord_1_1);
					p_object->normal(normal);
					// East Down - Inside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(in_tex_coord_0_1);
					p_object->normal(normal);
					// East Up - Inside
					p_object->position(v2);
					p_object->textureCoord(in_tex_coord_0_0);
					p_object->normal(normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), normal));
					normal_vec.push_back(NormalTuple_t(v2, normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// West (0, 1, 2)
					p_object->triangle(vertex_count - 4, vertex_count - 3,  vertex_count - 2);
					// East (2, 3, 0)
					p_object->triangle(vertex_count - 2 , vertex_count - 1, vertex_count - 4);
				}
				break;
				// Left corner
				//   --<
				//   |
			case Wall::OD_ES:
				{
					// South Center
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// South Side
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// East Side
					Ogre::Vector3 const v3(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v3.z + ((tile_v2.z - tile_v3.z) / 2));
					// East Center
					Ogre::Vector3 const v4(tile_v2.x, height, tile_v1.z);

					Ogre::Vector3 const south_normal = (v2 - v1).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();
					Ogre::Vector3 const east_normal = Ogre::Vector3::NEGATIVE_UNIT_Y.crossProduct(v3 - v4).normalisedCopy();

					// South Centre Up - Inside
					p_object->position(v1);
					// Beginning half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_1_0.y);
					p_object->normal(south_normal);
					// South Centre Down - Inside
					p_object->position(v1.x, 0, v1.z);
					// Beginning half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_1_1.y);
					p_object->normal(south_normal);
					// South Side Down - Inside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(in_tex_coord_0_1);
					p_object->normal(south_normal);
					// South Side Up - Inside
					p_object->position(v2);
					p_object->textureCoord(in_tex_coord_0_0);
					p_object->normal(south_normal);

					// East Side Up - Inside
					p_object->position(v3);
					p_object->textureCoord(in_tex_coord_1_0);
					p_object->normal(east_normal);
					// East Side Down - Inside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(in_tex_coord_1_1);
					p_object->normal(east_normal);
					// East Center Down - Inside
					p_object->position(v4.x, 0, v4.z);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_1.y);
					p_object->normal(east_normal);
					// East Center Up - Inside
					p_object->position(v4);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_0.y);
					p_object->normal(east_normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, south_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), south_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), south_normal));
					normal_vec.push_back(NormalTuple_t(v2, south_normal));

					normal_vec.push_back(NormalTuple_t(v3, east_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), east_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), east_normal));
					normal_vec.push_back(NormalTuple_t(v4, east_normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// South Side (3, 2, 1)
					p_object->triangle(vertex_count - 5, vertex_count - 6, vertex_count - 7);
					// South Center (3, 1, 0)
					p_object->triangle(vertex_count - 5, vertex_count - 7, vertex_count - 8);
					// West Side (6, 5, 4)
					p_object->triangle(vertex_count - 2, vertex_count - 3, vertex_count - 4);
					// West Center (6, 4, 7)
					p_object->triangle(vertex_count - 2, vertex_count - 4, vertex_count - 1);
				}
				break;
			case Wall::OD_EN:
				{
					// North Center
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// North Side
					Ogre::Vector3 const v2(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// North Side
					Ogre::Vector3 const v3(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// East Center
					Ogre::Vector3 const v4(tile_v0.x, height, tile_v1.z);

					Ogre::Vector3 const north_normal = (v2 - v1).crossProduct(Ogre::Vector3::UNIT_Y).normalisedCopy();
					Ogre::Vector3 const east_normal = (v3 - v4).crossProduct(Ogre::Vector3::UNIT_Y).normalisedCopy();

					// North Center Up - outside
					p_object->position(v1);
					// Ending half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_0.y);
					p_object->normal(north_normal);
					// North Center Down - outside
					p_object->position(v1.x, 0, v1.z);
					// Ending half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_0_1.y);
					p_object->normal(north_normal);
					// North Side Down - outside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(out_tex_coord_1_1);
					p_object->normal(north_normal);
					// North Side Up - outside
					p_object->position(v2);
					p_object->textureCoord(out_tex_coord_1_0);
					p_object->normal(north_normal);

					// East Side Up - Inside
					p_object->position(v3);
					p_object->textureCoord(in_tex_coord_1_0);
					p_object->normal(east_normal);
					// East Side Down - Inside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(in_tex_coord_1_1);
					p_object->normal(east_normal);
					// East Center Down - Inside
					p_object->position(v4.x, 0, v4.z);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_1.y);
					p_object->normal(east_normal);
					// East Center Up - Inside
					p_object->position(v4);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_0.y);
					p_object->normal(east_normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), north_normal));
					normal_vec.push_back(NormalTuple_t(v2, north_normal));

					normal_vec.push_back(NormalTuple_t(v3, east_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), east_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), east_normal));
					normal_vec.push_back(NormalTuple_t(v4, east_normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// North Center (0, 1, 3);
					p_object->triangle(vertex_count - 8, vertex_count - 7, vertex_count - 5);
					// North Side (1, 2, 3)
					p_object->triangle(vertex_count - 7, vertex_count - 6, vertex_count - 5);
					// East Center (7, 6, 5);
					p_object->triangle(vertex_count - 1, vertex_count - 2, vertex_count - 3);
					// East Side (7, 5, 4);
					p_object->triangle(vertex_count - 1, vertex_count - 3, vertex_count - 4);
				}
				break;
			case Wall::OD_NW:
				{
					// West Centre
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// West Side
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// North Side
					Ogre::Vector3 const v3(tile_v0.x + ((tile_v3.x - tile_v0.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// North Centre
					Ogre::Vector3 const v4(tile_v0.x, height, tile_v1.z);

					Ogre::Vector3 const west_normal = (v2 - v1).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();
					Ogre::Vector3 const north_normal = (v3 - v4).crossProduct(Ogre::Vector3::UNIT_Y).normalisedCopy();

					// West Center Up - Inside
					p_object->position(v1);
					// Beginning half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_1.y);
					p_object->normal(west_normal);
					// West Center Down - Inside
					p_object->position(v1.x, 0, v1.z);
					// Beginning half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_1_1.y);
					p_object->normal(west_normal);
					// West Side Down - Inside
					p_object->position(v2.x, 0, v2.z);
					p_object->textureCoord(in_tex_coord_0_1);
					p_object->normal(west_normal);
					// West Side Up - Inside
					p_object->position(v2);
					p_object->textureCoord(in_tex_coord_0_0);
					p_object->normal(west_normal);

					// North wall Up - Inside
					p_object->position(v3);
					p_object->textureCoord(in_tex_coord_1_0);
					p_object->normal(north_normal);
					// North Wall Down - Inside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(in_tex_coord_1_1);
					p_object->normal(north_normal);
					// North Center Down - Inside
					p_object->position(v4.x, 0, v4.z);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_1.y);
					p_object->normal(north_normal);
					// North Center Up - Inside
					p_object->position(v4);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_0.y);
					p_object->normal(north_normal);

#if HE_WALL_NORMALS_SHOW == 1						
					normal_vec.push_back(NormalTuple_t(v1, west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), west_normal));
					normal_vec.push_back(NormalTuple_t(v2, west_normal));

					normal_vec.push_back(NormalTuple_t(v3, north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), north_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), north_normal));
					normal_vec.push_back(NormalTuple_t(v4, north_normal));
#endif						
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// West Center (3, 1, 0);
					p_object->triangle(vertex_count - 5, vertex_count - 7, vertex_count - 8);
					// West Side (2, 1, 3);
					p_object->triangle(vertex_count - 6, vertex_count - 7, vertex_count - 5);
					// North Centre (7, 6, 5);
					p_object->triangle(vertex_count - 1, vertex_count - 2, vertex_count - 3);
					// North Side (7, 5, 4);
					p_object->triangle(vertex_count - 1, vertex_count - 3, vertex_count - 4);
				}
				break;
			case Wall::OD_WS:
				{
					// South Center
					Ogre::Vector3 const v1(tile_v0.x, height, tile_v1.z);
					// South Side
					Ogre::Vector3 const v2(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v1.z + ((tile_v2.z - tile_v1.z) / 2));
					// West Side
					Ogre::Vector3 const v3(tile_v1.x + ((tile_v0.x - tile_v1.x) / 2), height, tile_v0.z + ((tile_v1.z - tile_v0.z) / 2));
					// West Center
					Ogre::Vector3 const v4(tile_v2.x, height, tile_v3.z);

					Ogre::Vector3 const south_normal = (v2 - v1).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();
					Ogre::Vector3 const west_normal = (v3 - v4).crossProduct(Ogre::Vector3::NEGATIVE_UNIT_Y).normalisedCopy();

					// South Center Up - Inside
					p_object->position(v1);
					p_object->textureCoord(in_tex_coord_1_0);
					p_object->normal(south_normal);
					// South Center Down - Inside
					p_object->position(v1.x, 0, v1.z);
					p_object->textureCoord(in_tex_coord_1_1);
					p_object->normal(south_normal);
					// South Side Down - Inside
					p_object->position(v2.x, 0, v2.z);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_1.y);
					p_object->normal(south_normal);
					// South Side Up - Inside
					p_object->position(v2);
					// Ending half of texture
					p_object->textureCoord(in_tex_coord_0_0.x + (std::get<2>(texture_info_inside) / 2), in_tex_coord_0_0.y);
					p_object->normal(south_normal);

					// West Side Up - Outside
					p_object->position(v3);
					p_object->textureCoord(out_tex_coord_0_0);
					p_object->normal(west_normal);
					// West Side Down - Outside
					p_object->position(v3.x, 0, v3.z);
					p_object->textureCoord(out_tex_coord_0_1);
					p_object->normal(west_normal);
					// West Center Down - Outside
					p_object->position(v4.x, 0, v4.z);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_1.y);
					p_object->normal(west_normal);
					// West Center Up - Outside
					p_object->position(v4);
					// Beginning half of texture
					p_object->textureCoord(out_tex_coord_0_0.x + (std::get<2>(texture_info_outside) / 2), out_tex_coord_1_0.y);
					p_object->normal(west_normal);

#if HE_WALL_NORMALS_SHOW == 1
					normal_vec.push_back(NormalTuple_t(v1, south_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v1.x, 0, v1.z), south_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v2.x, 0, v2.z), south_normal));
					normal_vec.push_back(NormalTuple_t(v2, south_normal));

					normal_vec.push_back(NormalTuple_t(v3, west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v3.x, 0, v3.z), west_normal));
					normal_vec.push_back(NormalTuple_t(Ogre::Vector3(v4.x, 0, v4.z), west_normal));
					normal_vec.push_back(NormalTuple_t(v4, west_normal));
#endif
					// Create walls
					size_t const vertex_count = p_object->getCurrentVertexCount();
					// South Center(3, 1, 0);
					p_object->triangle(vertex_count - 5, vertex_count - 7, vertex_count - 8);
					// South Side (3, 2, 1);
					p_object->triangle(vertex_count - 5, vertex_count - 6, vertex_count - 7);
					// West Side (4, 5, 6);
					p_object->triangle(vertex_count - 4, vertex_count - 3, vertex_count - 2);
					// West Center (4, 6, 7);
					p_object->triangle(vertex_count - 4, vertex_count - 2, vertex_count - 1);
				}
				break;
			}
			// Update next tile
			switch((*it)->orientation())
			{

			case Wall::OD_NS:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::SOUTH_WEST);
				break;
			case Wall::OD_WE:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::SOUTH_EAST);
				break;
			case Wall::OD_SE:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::SOUTH_EAST);
				break;
			case Wall::OD_NE:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::SOUTH_EAST);
				break;
			case Wall::OD_WN:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::NORTH_EAST);
				break;
			case Wall::OD_SW:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::NORTH_WEST);
				break;
			case Wall::OD_SN:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::NORTH_EAST);
				break;
			case Wall::OD_EW:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::NORTH_WEST);
				break;
			case Wall::OD_ES:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::SOUTH_WEST);
				break;
			case Wall::OD_EN:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::NORTH_EAST);
				break;
			case Wall::OD_NW:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::NORTH_WEST);
				break;
			case Wall::OD_WS:
				// Update next tile
				p_base_tile = m_Map.get_neighbor(*p_base_tile, TileWalker::SOUTH_WEST);
				break;
			}
		}
		p_object->end();
		// Create scene node for building
		Ogre::SceneNode *p_scene_node = m_SceneManager.getRootSceneNode()->createChildSceneNode();
		// Attach to node
		p_scene_node->attachObject(p_object);

#if HE_WALL_NORMALS_SHOW == 1
		// Add normals
		Ogre::ManualObject *p_normals = m_SceneManager.createManualObject();
		p_normals->begin("he/normal", Ogre::RenderOperation::OT_LINE_LIST);
		p_normals->setRenderQueueGroup(p_object->getRenderQueueGroup() + 1);
		for(auto it = normal_vec.begin(), end = normal_vec.end(); it != end; ++it)
		{
			p_normals->position(std::get<0>(*it));
			p_normals->position(std::get<0>(*it) + std::get<1>(*it));

			size_t const vertex_count = p_normals->getCurrentVertexCount();

			p_normals->index(vertex_count - 2);
			p_normals->index(vertex_count - 1);
		}
		p_normals->end();
		m_SceneManager.getRootSceneNode()->createChildSceneNode()->attachObject(p_normals);
#endif
	}
} /*he*/
